package com.northpool.operator.analysis.proximity;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.northpool.operator.analysis.utils.DatasetUtils;
import com.northpool.operator.annotation.InputParam;
import com.northpool.operator.annotation.InputParams;
import com.northpool.operator.annotation.OperatorInfo;
import com.northpool.operator.common.algorithm.OperatorTestBuild;
import com.northpool.operator.common.algorithm.TestParamBuilder;
import com.northpool.operator.param.ParamTool;
import com.northpool.operator.param.custom.DatasetParam;
import com.northpool.operator.type.TypeEnum;
import com.northpool.operator.utils.StpOperatorHelper;
import com.northpool.resources.Constants;
import com.northpool.resources.datatable.operate.TableSchemaBean;
import com.northpool.spatial.Geom;
import org.locationtech.jts.geom.Geometry;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@OperatorInfo(
        ref = "NearOperator",
        name = "周边",
        version = "1.0.0-dataset",
        description = "可计算输入要素与其他图层或要素类中的最近要素之间的距离和其他邻近性信息。"
)
public class NearOperator extends StpOperatorHelper {

    @InputParams({
            @InputParam(ref = "in_dataset", name = "输入数据集", desc = "输入数据集", type = TypeEnum.DATASET, required = true),
            @InputParam(ref = "near_dataset", name = "周边数据集", desc = "输入数据集", type = TypeEnum.DATASET, required = true),
            @InputParam(ref = "out_dataSourceId", name = "输出数据源", desc = "输出数据源ID", type = TypeEnum.DATASOURCE, required = true),
            @InputParam(ref = "out_table", name = "输出数据表名称", desc = "输出数据表名称， 如： out_table", required = true),
            @InputParam(ref = "search_radius", name = "搜索半径", desc = "搜索半径数值，单位米或度，如：100", required = true, type = TypeEnum.NUMBER),
    })
    public NearOperator() {
    }

    public static void main(String[] args) {
        OperatorTestBuild.execute(NearOperator.class, TestParamBuilder.aBuilder()
                .param("in_dataset", "{\"dataSourceId\": \"kjklz\", \"tableName\": \"public.bj_places\"}")
                .param("near_dataset", "{\"dataSourceId\": \"kjklz\", \"tableName\": \"public.bj_places\"}")
                .param("out_dataSourceId", "kjklz")
                .param("out_table", "result.point_distance_2")
                .param("search_radius", "0.01")
                .build());
    }

    @Override
    public void execute(ParamTool paramTool) throws Exception {
        DatasetParam inDataset = paramTool.getDataset("in_dataset");
        DatasetParam nearDataset = paramTool.getDataset("near_dataset");
        String outDataSourceId = paramTool.getStr("out_dataSourceId");
        String outTable = paramTool.getStr("out_table");
        BigDecimal searchRadius = paramTool.getNumber("search_radius");
        // 是否是同一数据集
        boolean isSameDataset = inDataset.equals(nearDataset);

        // 字段位置获取
        int inGeomIndex = DatasetUtils.getSpatialIndex(inDataset);
        int nearGeomIndex = DatasetUtils.getSpatialIndex(nearDataset);
        int inIdIndex = DatasetUtils.getIdIndex(inDataset);
        int nearIdIndex = DatasetUtils.getIdIndex(nearDataset);

        // 创建结果表
        Constants.DATA_SOURCE_TYPE dataSourceType = DatasetUtils.dataSourceType(outDataSourceId);

        List<String> extentFieldList = ListUtil.of(
                "NEAR_FID,输入要素的要素ID,String,255,0,false,false,",
                "DISTANCE,输入要素与邻近要素之间的距离,Double,50,0,false,false,"
        );
        TableSchemaBean tableSchemaBean = DatasetUtils.createTableSchemaBean(dataSourceType, outTable, "周边分析结果表", extentFieldList);
        if (DatasetUtils.tableExists(outDataSourceId, outTable)) {
            this.error("输出表 {} 已存在,请先删除表或输出到新表", outTable);
            this.failedExit();
        } else {
            DatasetUtils.createTable(outDataSourceId, tableSchemaBean);
        }

        // 数据集信息获取
        List<Object[]> inRows = DatasetUtils.queryDataset(inDataset);
        List<Object[]> nearRows = DatasetUtils.queryDataset(nearDataset);
        Map<String, Double> minDistanceMap = new HashMap<>();

        List<Object[]> rows = new ArrayList<>();
        for (Object[] inRow : inRows) {
            Geom inGeom = (Geom) inRow[inGeomIndex];
            String inID = StrUtil.toString(inRow[inIdIndex]);
            Geometry inJtsGeometry = inGeom.toJTSGeometry();
            String nearId = "-1";
            Double nearDist = -1D;

            for (Object[] nearRow : nearRows) {
                String id = StrUtil.toString(nearRow[nearIdIndex]);
                if (isSameDataset && Objects.equals(inID, id)) {
                    continue; // 跳过相同的主键的数据
                }
                Geom nearGeom = (Geom) nearRow[nearGeomIndex];
                Geometry nearJtsGeometry = nearGeom.toJTSGeometry();
                double distance = inJtsGeometry.distance(nearJtsGeometry);
                if (distance <= searchRadius.doubleValue()) {
                    if (nearDist < 0 || distance < nearDist) {
                        nearId = id;
                        nearDist = distance;
                    }
                }
            }
            Object[] row = ArrayUtil.addAll(inRow, new Object[]{
                    nearId, nearDist
            });
            rows.add(row);
        }
        DatasetUtils.writeRows(outDataSourceId, outTable, tableSchemaBean.getColumnMap().keySet().toArray(new String[0]), rows);
        this.log("分析完成");
    }
}
